package water.com.Ioc;

import water.com.Log.Log;
import water.com.util.ConfigurationReader;

import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Bean注册表类,此类的职责是扫描所有文件，
 * 将疑似可加载的文件信息存入注册表并进行Ioc容器初始化
 */
public class WaterBeanRegistryInfo {
    /**
     * 可实例化类信息注册表,存储所有可以实例化的类信息，方便实例化（无需使用new进行实例化）
     * key--简单类名  value--全类名
     */
    public static Map<String,String> registryMap;

    public static void scanPackage(){
        String scanPath = (String) ConfigurationReader.getInstance().getValueByKey("server","scanPath");
        registryMap = new HashMap<>();
        try{
            if(scanPath.contains("/"))
                listTempFiles(new File(scanPath));
            else searchClass(scanPath);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void initRegistryMap() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        scanPackage();
        new WaterBeanFactory().initBeanFactory();
        Log.getLog().info("可实例化类信息注册表大小:"+registryMap.size());
    }

    /**
     * 递归遍历源码文件夹，并将java文件筛选出来
     * 结息其信息，将其类名和包路径存储注册表中，方便后面根据类名获取类实例
     * @param  root 根目录
     */
    private static void listTempFiles(File root) {
        // 获取指定磁盘或文件夹的子列表
        File[] files = root.listFiles();
        if (files == null)
            return;
        for (File file : files) {
            if (file.isFile()) {
                if(file.getName().contains(".class")){
                    String beanName = file.getName().replace(".class","");
                    String[] packages = file.getPath().split("\\\\");
                    String packageStr = "";
                    for(int i = 5;i<packages.length;i++){
                        packageStr += packages[i]+".";
                    }
                    packageStr = packageStr.substring(0,packageStr.length()-1);
                    packageStr = packageStr.replace(".class","");
                    registryMap.put(beanName,packageStr);
                }
            } else if (file.isDirectory()){
                listTempFiles(file);
            }
        }
    }

    /**
     * 扫描jar中的类
     * @param basePack 包名
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static void searchClass(String basePack) throws IOException, ClassNotFoundException {
        //通过当前线程得到类加载器从而得到URL的枚举
        Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(basePack.replace(".", "/"));
        while (urlEnumeration.hasMoreElements()) {
            URL url = urlEnumeration.nextElement();//得到的结果大概是：jar:file:/C:/Users/ibm/.m2/repository/junit/junit/4.12/junit-4.12.jar!/org/junit
            String protocol = url.getProtocol();//大概是jar
            if ("jar".equalsIgnoreCase(protocol)) {
                //转换为JarURLConnection
                JarURLConnection connection = (JarURLConnection) url.openConnection();
                if (connection != null) {
                    JarFile jarFile = connection.getJarFile();
                    if (jarFile != null) {
                        //得到该jar文件下面的类实体
                        Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
                        while (jarEntryEnumeration.hasMoreElements()) {
                            /*entry的结果大概是这样：
                                    org/
                                    org/junit/
                                    org/junit/rules/
                                    org/junit/runners/*/
                            JarEntry entry = jarEntryEnumeration.nextElement();
                            String jarEntryName = entry.getName();
                            //这里我们需要过滤不是class文件和不在basePack包名下的类
                            if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/",".").startsWith(basePack)) {
                                String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", ".");
                                registryMap.put(className.substring(className.lastIndexOf(".")+1),className);
                            }
                        }
                    }
                }
            }
        }
    }


}
